package com.zm.xiaohashu.user.biz.runner;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zm.xiaohashu.user.biz.constant.RedisKeyConstants;
import com.zm.xiaohashu.user.biz.mapper.PermissionMapper;
import com.zm.xiaohashu.user.biz.mapper.RoleMapper;
import com.zm.xiaohashu.user.biz.mapper.RolePermissionRelMapper;
import com.zm.xiaohashu.user.biz.model.dto.Permission;
import com.zm.xiaohashu.user.biz.model.dto.Role;
import com.zm.xiaohashu.user.biz.model.dto.RolePermissionRel;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class PushRolePermissions2RedisRunner implements ApplicationRunner {
    @Resource
    private RoleMapper roleMapper;
    @Resource
    private PermissionMapper permissionMapper;
    @Resource
    private RolePermissionRelMapper rolePermissionRelMapper;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource(name = "taskExecutor")
    private ThreadPoolTaskExecutor executor;
    // 权限同步标记 Key
    private static final String PUSH_PERMISSION_FLAG = "push.permission.flag";

    @Override
    public void run(ApplicationArguments args) {
        // 是否能够同步数据: 原子操作，只有在键 PUSH_PERMISSION_FLAG 不存在时，才会设置该键的值为 "1"，并设置过期时间为 1 天
        boolean canPushed = redisTemplate.opsForValue().setIfAbsent(PUSH_PERMISSION_FLAG, "1", 1, TimeUnit.DAYS);
        // 如果无法同步权限数据
        if (!canPushed) {
            log.warn("==> 角色权限数据已经同步至 Redis 中，不再同步...");
            return;
        }
        executor.submit(() -> {
            log.info("==> 服务启动，开始同步角色权限数据到 Redis 中...");
            LambdaQueryWrapper<Role> roleLqw = new LambdaQueryWrapper<>();
            roleLqw.eq(Role::getIsDeleted, 0);
            List<Role> roleList = roleMapper.selectList(roleLqw);//可用的角色列表
            if (CollectionUtil.isEmpty(roleList)) {
                log.info("==> 服务启动，没有可用的角色数据...");
                return;
            }
            List<Long> roleIds = roleList.stream().map(role -> role.getId()).toList();
            for (Long roleId : roleIds) {
                LambdaQueryWrapper<RolePermissionRel> rolePermissonLqw = new LambdaQueryWrapper<>();
                rolePermissonLqw.eq(RolePermissionRel::getIsDeleted, 0);
                rolePermissonLqw.eq(RolePermissionRel::getRoleId, roleId);
                List<RolePermissionRel> rolePermissionRelList = rolePermissionRelMapper.selectList(rolePermissonLqw);
                if (CollectionUtil.isEmpty(rolePermissionRelList)) {
                    log.info("==> 服务启动，没有可用的角色权限数据...");
                    return;
                }
                //一个角色对应的多个权限
                List<Long> permissionIds = rolePermissionRelList.stream().map(rolePermissionRel -> rolePermissionRel.getPermissionId()).toList();
                Role role = roleMapper.selectById(roleId);
                String permissionKey = RedisKeyConstants.buildRolePermissionsKey(role.getRoleKey());
                ArrayList<String> permissionList = new ArrayList<>();
                permissionIds.forEach(permissionId -> {
                    Permission permission = permissionMapper.selectById(permissionId);
                    permissionList.add(permission.getPermissionKey());
                });
                //过期时间保底一天加随机秒数
                int expire = 60 * 60 * 24 + RandomUtil.randomInt(60 * 60 * 24);
                redisTemplate.opsForValue().set(permissionKey, JSONUtil.toJsonStr(permissionList), expire, TimeUnit.SECONDS);
            }
            log.info("==> 服务启动，成功同步角色权限数据到 Redis 中...");
        });
    }

}